﻿namespace XDCCManager.Model
{
	public class SortedObservableCollection<T> : System.Collections.Generic.IList<T>, System.Collections.Specialized.INotifyCollectionChanged, System.ComponentModel.INotifyPropertyChanged where T : class, Packs.IPack, System.ComponentModel.INotifyPropertyChanged
	{
		System.Collections.Generic.IList<T> internalList;

		public SortedObservableCollection()
		{
			internalList = new System.Collections.Generic.List<T>();
		}

		~SortedObservableCollection()
		{
			Clear();
		}

		#region IList<T> Members

		public int IndexOf(T item)
		{
			return internalList.IndexOf(item);
		}

		public void Insert(int index, T item)
		{
			if (internalList.Contains(item))
				throw new System.ArgumentException("item already in the list.");
			if (index >= internalList.Count)
				index = internalList.Count;
			else if (index < 0)
				index = 0;
			item.Number = index + 1;
			item.PropertyChanged += item_PropertyChanged;
			internalList.Insert(index, item);
			OnCollectionChanged(new System.Collections.Specialized.NotifyCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedAction.Add, item, index));
			OnPropertyChanged("Count");
		}

		void item_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
		{
			if (e.PropertyName != "Number")
				return;
			T item = sender as T;
			int oldIndex = internalList.IndexOf(item);
			if (oldIndex < 0)
			{
				System.Diagnostics.Debug.WriteLine("Collection listening to an external item.");
				return;
			}
			int index = item.Number - 1;
			if (oldIndex == index)
				return;
			if (oldIndex < index)
			{
				for (int i = oldIndex; i < index; i++)
				{
					internalList[i] = internalList[i + 1];
					internalList[i].Number = i + 1;
				}
			}
			else
			{
				for (int i = oldIndex; i > index; i--)
				{
					internalList[i] = internalList[i - 1];
					internalList[i].Number = i + 1;
				}
			}
			internalList[index] = item;
		}

		public void RemoveAt(int index)
		{
			if ((index < 0) || (index >= internalList.Count))
				return;
			T item = internalList[index];
			item.PropertyChanged -= item_PropertyChanged;
			internalList.RemoveAt(index);
			OnCollectionChanged(new System.Collections.Specialized.NotifyCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedAction.Remove, item, index));
			OnPropertyChanged("Count");
			for (int i = index; i < internalList.Count; i++)
				internalList[i].Number = i + 1;
		}

		public T this[int index]
		{
			get
			{
				return internalList[index];
			}
			set
			{
				throw new System.NotSupportedException();
			}
		}

		#endregion

		#region ICollection<T> Members

		public void Add(T item)
		{
			Insert(internalList.Count, item);
		}

		public void Clear()
		{
			System.Collections.Generic.IList<T> items = new System.Collections.Generic.List<T>(internalList);
			foreach (var item in items)
				item.PropertyChanged -= new System.ComponentModel.PropertyChangedEventHandler(item_PropertyChanged);
			internalList.Clear();
			OnCollectionChanged(new System.Collections.Specialized.NotifyCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedAction.Remove, (System.Collections.IList)items, 0));
			OnPropertyChanged("Count");
		}

		public bool Contains(T item)
		{
			return internalList.Contains(item);
		}

		public void CopyTo(T[] array, int arrayIndex)
		{
			internalList.CopyTo(array, arrayIndex);
		}

		public int Count
		{
			get { return internalList.Count; }
		}

		public bool IsReadOnly
		{
			get { return false; }
		}

		public bool Remove(T item)
		{
			int index = IndexOf(item);
			if (index >= 0)
				RemoveAt(index);
			return index >= 0;
		}

		#endregion

		#region IEnumerable<T> Members

		public System.Collections.Generic.IEnumerator<T> GetEnumerator()
		{
			return internalList.GetEnumerator();
		}

		#endregion

		#region IEnumerable Members

		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return (internalList as System.Collections.IEnumerable).GetEnumerator();
		}

		#endregion

		#region INotifyCollectionChanged Members

		public event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged;

		#endregion

		private void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs args)
		{
			if (CollectionChanged != null)
				CollectionChanged(this, args);
		}

		#region INotifyPropertyChanged Members

		public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

		#endregion

		private void OnPropertyChanged(string name)
		{
			if (PropertyChanged != null)
				PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(name));
		}
	}
}
